package program.commands;

import be.kuleuven.cs.som.annotate.*;
import attribute.*;
import item.*;

/**
 * A class concerning turn-commands: (turn clockwise) or (turn counter-clockwise).
 *
 * @invar	The Orientation of each turn-command must be valid.
 *           | canHaveAsOrientation(getOrientation())
 *
 */
public class Turn extends BasicCommand {
	private final Orientation orientation;

	/**
	 * Initialise this new turn-command with a line number an Orientation.
	 *
	 * @param       Orientation
	 *                      The orientation associated with this turn-command.
	 * @pre         The given orientation must be a valid one.
	 *                      | canHaveAsOrientation(Orientation)
	 * @post        The orientation of this turn-command is set to the given orientation.
	 *                      | new.getOrientation() == Orientation
	 */
	public Turn(int lineNb, String string) {
		super(lineNb);
		Orientation newOrientation = turnToOrientation(string);
		assert(canHaveAsOrientation(newOrientation));
		this.orientation = newOrientation;
	}

	
	/**
	 * 
	 * @param string
	 * 
	 * @return
	 * 
	 */
	public Orientation turnToOrientation(String string) {
		Orientation changeToOrientation = null;
		int clockOrCounter = 0;
		if(string.equals("clockwise")) clockOrCounter = -1;
		if(string.equals("counterClockwise")) clockOrCounter = 1;
		switch(Orientation.values().toString()) {
		case "UP":changeToOrientation = toOrientation(Orientation.UP.ordinal() + clockOrCounter);
		case "RIGHT":changeToOrientation = toOrientation(Orientation.RIGHT.ordinal() + clockOrCounter);
		case "DOWN":changeToOrientation = toOrientation(Orientation.DOWN.ordinal() + clockOrCounter);
		case "LEFT":changeToOrientation = toOrientation(Orientation.LEFT.ordinal() + clockOrCounter);}
		return changeToOrientation;
	}

	/**
	 * Converts the given integer orientation into an Orientation.
	 * 
	 * @param orientation
	 * 			The integer that must be converted.
	 * 
	 * @return 
	 * 			|orienation = orientation % 4
	 * 			|if(orientation < 0) orientation += 4
	 * 			if the orientation equals zero, the orientation is set up.
	 * 			|if(orientation == 0) then result == Orientationation.UP;
	 * 			if the orientation equals one, the orientation is set right.
	 * 			|if(orientation == 1) then result == Orientationation.RIGHT;
	 * 			if the orientation equals two, the orientation is set down.
	 * 			|if(orientation == 2) then result == Orientationation.DOWN;
	 * 			if the orientation equals three, the orientation is set left.
	 * 			|if(orientation == 3) then result == Orientationation.LEFT;
	 */
	public Orientation toOrientation(int orientation){
		orientation = orientation % 4;
		if(orientation < 0) orientation += 4;
		if(orientation == 0)
			return Orientation.UP;
		else if(orientation == 1)
			return Orientation.RIGHT;
		else if(orientation == 2)
			return Orientation.DOWN;
		else
			return Orientation.LEFT;
	}
	
	/**
	 * Returns the orientation associated with this turn-command.
	 */
	@Basic @Raw @Immutable
	public Orientation getOrientation() {
		return this.orientation;
	}
	/**
	 * Checks whether the given orientation is a valid orientation for a turn-command.
	 *
	 * @param	orientation
	 *           The Orientation to check for whether it is a valid one.
	 * @return	
	 *			| result == ( Orientation == Orientation.CLOCKWISE ||
	 *           |                               Orientation == Orientation.COUNTER_CLOCKWISE )
	 */
	@Raw
	public boolean canHaveAsOrientation(Orientation orientation) {
		return ( getOrientation() == Orientation.DOWN || getOrientation() == Orientation.UP
				|| getOrientation() == Orientation.LEFT || getOrientation() == Orientation.RIGHT);
	}

	/**
	 * Return a textual representation of this turn-command,
	 * formatted in accordance with its position in the program.
	 *
	 * @return	The result is a pretty formatted string of this turn-command,
	 *          taking in account his relative position in the program.
	 *          | if(getOrientation() == Orientation.CLOCKWISE)
	 *          |       then result == getIndentation(indentationLevel) + "(turn clockwise)"
	 *          | if(getOrientation() == Orientation.COUNTER_CLOCKWISE)
	 *          |       then result == getIndentation(indentationLevel) + "(turn counter-clockwise)"
	 */
	@Override
	public String prettyFormat(int indentationLevel) {
		if(getOrientation() == turnToOrientation("clockwise")) 
			return getIndentation(indentationLevel) + "(turn clockwise)";
		return getIndentation(indentationLevel) + "(turn counter-clockwise)";
	}

	/**
	 * Execute the turn-command on the given robot.
	 *
	 * @effect	The given robot turns in the Orientation associated with this turn-command.
	 *                      | robot.turn(getOrientation())
	 */
	@Override
	public void execute(Robot robot) throws IllegalArgumentException {
		super.execute(robot);
		robot.setOrientationTo(getOrientation());
	}
}